Skip to content

[FIX] connector_woocommerce_ast: gate partial_shipped on tracking ready#894

Merged
eantones merged 1 commit into14.0from
14.0-fix-connector_woocommerce_ast-gate_partial_shipped_on_tracking_ready
May 1, 2026
Merged

[FIX] connector_woocommerce_ast: gate partial_shipped on tracking ready#894
eantones merged 1 commit into14.0from
14.0-fix-connector_woocommerce_ast-gate_partial_shipped_on_tracking_ready

Conversation

@eantones
Copy link
Copy Markdown
Member

@eantones eantones commented Apr 23, 2026

Context

Alternative to #889 commit bc4057ba (status-mapper gate). Same root cause, fix placed at the state definition instead of the transform.

Related internal task: connector WooCommerce — propagate state and tracking on partial deliveries.

Bug mechanism

Inside stock.picking._action_done there are two separate writes on the picking in one transaction:

  • Write A (early, in core stock): picking.state = "done"
  • Write B (later, inside _send_confirmation_emailsend_to_shipper): picking.carrier_tracking_ref = ref, after the blocking carrier API call

sale.order.woocommerce_order_state is store=True with @api.depends("picking_ids.state", ...). Any ORM flush between A and B (backorder creation, move_line writes, message_post, etc.) materializes an intermediate state where state="done" and carrier_tracking_ref=False.

At that moment _get_woocommerce_order_state promotes the order to partial_shipped, on_compute_woocommerce_order_state fires, and export_batch(delayed=False) exports synchronously. Payload reaches WooCommerce with status=partial-shipped and no tracking number → WC fires the "partially shipped" email without tracking.

When Write B lands, stock_picking.write fires the event again → second export updates the metadata, but the email was already sent.

Fix

Gate the processing → partial_shipped transition on having carrier_tracking_ref set for all done pickings whose carrier calls send_to_shipper (integration_level == "rate_and_ship"). Pickings on fixed-rate carriers or without a carrier never auto-populate the ref, so they pass through unchanged.

Also add picking_ids.carrier_tracking_ref to _compute_woocommerce_order_state's @api.depends so the state re-evaluates when the ref lands and the single correct export fires.

Why this over PR #889's bc4057ba

bc4057ba (status mapper gate) This PR (state-compute gate)
Where export_mapper.pystatus transform sale_order.py_get_woocommerce_order_state
Transitions per partial 2 exports (first returns None, second sends real) 1 export
Internal Odoo state during race Briefly partial_shipped with no ref — inconsistent with what WC sees Stays processing until ref is ready
@api.depends re-trigger N/A (mapper reads existing state) Yes — adds picking_ids.carrier_tracking_ref

Known trade-off

If the carrier API ever returns successfully with an empty NumeroEnvio/tracking ref, the order freezes at processing on WC. In practice for MRW/SEUR this shouldn't happen — the API either returns a tracking number or raises an error (which would fail _action_done entirely, so the picking wouldn't reach done). bc4057ba has the same failure mode at the mapper layer.

Test plan

  • Partial delivery with an MRW-carrier picking — verify the partial-shipped email includes the tracking number
  • Partial delivery with a fixed-rate / no-carrier picking — verify it still promotes to partial_shipped without blocking
  • Complete delivery (all pickings done with tracking) — verify promotion to delivered with full payload
  • Order 19122-style scenario (field-reported)

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 7.69231% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.39%. Comparing base (a65d4f1) to head (5354f94).
⚠️ Report is 1 commits behind head on 14.0.

Files with missing lines Patch % Lines
...woocommerce_ast/models/sale_order/export_mapper.py 0.00% 12 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             14.0     #894      +/-   ##
==========================================
+ Coverage   50.37%   50.39%   +0.01%     
==========================================
  Files        1175     1175              
  Lines       20100    20098       -2     
  Branches     4271     4271              
==========================================
+ Hits        10126    10129       +3     
+ Misses       9741     9736       -5     
  Partials      233      233              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@eantones eantones force-pushed the 14.0-fix-connector_woocommerce_ast-gate_partial_shipped_on_tracking_ready branch from 5595e44 to 5fc548e Compare April 23, 2026 14:05
@eantones
Copy link
Copy Markdown
Member Author

Closing without merge.

Investigation on test3 (deployed PRs on ./addons/nuobit-odoo-addons per /var/opt/odoo114/repos.yaml: #635, #887, #889, #890without #894) confirmed that the "parcialment enviat" email on the first partial MRW delivery already arrives with the tracking number. The race inside stock.picking._action_done (Write A: state=done before Write B: carrier_tracking_ref) is handled by the existing two-export pattern:

  1. Flush inside MRW's mrw_create_shippingmessage_post triggers _compute_woocommerce_order_state → state promotes to partial_shippedexport Migration to version 12.0 #1 goes to WooCommerce with status=partial-shipped and _wc_shipment_tracking_items=[{tracking_provider}] (no tracking_number yet).
  2. Back in send_to_shipper, self.carrier_tracking_ref = res['tracking_number'] triggers the explicit notify in connector_woocommerce_ast/models/stock_picking/stock_picking.py:writeexport [12.0][IMP] product_service_time: added tests #2 with status=partial-shipped + full tracking payload.

AST Pro in WooCommerce uses export #2's tracking when firing the shipment email, so the customer receives the email with the tracking number. PR #889's customer_delivered mapping fix (commit 2db327ba) is what prevents an earlier path that would have short-circuited the promotion.

PR #894's state-compute gate was unnecessary and, empirically, broke the timing AST Pro relied on — with #894 applied, the email arrives without tracking. Closing as no-op.

The architectural refactor for a proper stock.picking connector binding (exporter + adapter + mapper) remains tracked separately in task #2567 on nuobit15-prod (Backlog, opt-in cleanup).

@eantones eantones closed this Apr 24, 2026
@eantones eantones reopened this Apr 29, 2026
@eantones
Copy link
Copy Markdown
Member Author

eantones commented Apr 29, 2026

🔁 PR reopened — 2026-04-29

Earlier closing was premature. A new field report shows the partial-shipped email arriving without tracking on a recent first-partial delivery (order 19238, 2026-04-28), while a 2026-04-24 reference order (19141) had it correctly. Bug is alive on test3.

Empirical verification on test3

19238 (broken) 19141 (OK)
Picking 1291464 (OSWSC/OUT/07110) 1291461 (OSWSC/OUT/07107)
write_date 2026-04-28 17:09:47 2026-04-24 10:16:45
MRW response (chatter) 17:09:52 10:16:49
WC customer email sent 17:09:47 (5s before MRW) 10:16:49 (same as MRW)
MRW URL in email ...&envio=&... (empty) ...&envio=00626F724930&...

Both exports happen synchronously (delayed=False in listener) — no queue.job rows. The variable is the network/processing latency between Odoo's compute-fired export #1 and AST Pro's email trigger on the WC side.

The race is genuinely timing-dependent, not a deterministic code path. PR #894's gate approach (defer state transition until tracking is set) was empirically observed to consistently produce emails without tracking, suggesting AST Pro relies on the processing → partial_shipped state-change event to fire the email at all.

Path forward

This PR stays open for discussion. Options:

A. Defer message_post inside mrw_create_shipping until after carrier_tracking_ref is written (touches MRW carrier).
B. Suppress export #1 at the listener level when partial-shipped without tracking is imminent.
C. Reorder writes inside _action_done so carrier_tracking_ref is set before the state transition.
D. Configure AST Pro on the WC side to delay or use a different email trigger.
E. Add a flag to export #1 that tells AST Pro "this is preliminary — don't fire the email yet".

Investigation continues. Will update with proposed approach.

@deeniiz deeniiz force-pushed the 14.0-fix-connector_woocommerce_ast-gate_partial_shipped_on_tracking_ready branch 3 times, most recently from 1d069cf to 25801b9 Compare April 30, 2026 15:47
@eantones eantones force-pushed the 14.0-fix-connector_woocommerce_ast-gate_partial_shipped_on_tracking_ready branch from 25801b9 to 5354f94 Compare May 1, 2026 22:37
@eantones eantones merged commit 6c9cf90 into 14.0 May 1, 2026
12 of 13 checks passed
@eantones eantones deleted the 14.0-fix-connector_woocommerce_ast-gate_partial_shipped_on_tracking_ready branch May 1, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant